use heapless::Vec;
use utils::MutCell;

use crate::c_src::osWaitForever;

use super::EventFlags;


const FLAG_LOCK: u32 = 0x01;
const FLAG_NOTIFY: u32 = 0x02;

pub struct MsgQueue<T, const SIZE: usize> {
    q: MutCell<Vec<T, SIZE>>,
    event_flags: EventFlags,
}

impl <T: Send + 'static, const SIZE: usize> MsgQueue<T, SIZE> {

    pub const fn uninit() -> Self {
        Self {
            q: MutCell::const_new(Vec::new()),
            event_flags: EventFlags::uninit(),
        }
    }

    pub unsafe fn init_once(&self) {
        self.event_flags.init_once("msg_queue");
        self.event_flags.set_flags(FLAG_LOCK);
    }

    pub fn post(&self, item: T) -> Result<(), T> {
        self.lock();
        let q = unsafe { self.q.as_mut() };
        let r = q.push(item);
        if r.is_ok() {
            self.notify();
        }
        self.unlock();
        r
    }

    pub fn take(&self) -> T {
        loop {
            self.lock();
            let q = unsafe { self.q.as_mut() };
            let ret = q.pop();
            self.unlock();
            if let Some(ret) = ret {
                return ret;
            }
            self.wait();
        }
    }

    pub fn poll(&self, timeout: u32) -> Option<T> {
        let mut flag = false;
        loop {
            self.lock();
            let q = unsafe { self.q.as_mut() };
            let ret = q.pop();
            self.unlock();
            if flag || ret.is_some() {
                return ret;
            }
            self.wait_with_timeout(timeout);
            flag = true
        }
    }

    fn lock(&self) {
        _ = self.event_flags.wait_any_flags(FLAG_LOCK, osWaitForever);
    }

    fn unlock(&self) {
        self.event_flags.set_flags(FLAG_LOCK);
    }

    fn notify(&self) {
        self.event_flags.set_flags(FLAG_NOTIFY);
    }

    fn wait(&self) {
        _ = self.event_flags.wait_any_flags(FLAG_NOTIFY, osWaitForever);
    }

    fn wait_with_timeout(&self, timeout: u32) -> bool {
        self.event_flags.wait_any_flags(FLAG_NOTIFY, timeout).is_ok()
    }
}








